== 软件项目 == 

160KM PIS服务器git仓库主页: https://git.g77k.com/qichunren/ntpis160

PIS服务器软件ntpis160有两个分支，分支1.x是以在与四方所PIS统型(2017-9)之前基于25t的界面风格和需要开发的，软件的运行环境是XUbuntu 14.04；分支master是目前正在应用的，按照四方所PIS界面和操作需求，在1.x分支后续开发的，软件的运行环境是ARM 335x，使用BuildRoot构建而成的嵌入式Linux系统 [https://git.g77k.com/yiling.cao/pudge-buildroot/tree/160km-pis-server 160km-pis-server]。

== PIS Server 1.x ==

[[160KM PIS车厢控制器项目]]的git仓库主页: https://git.g77k.com/qichunren/ntpis160-cabin-controller ,这个项目是用于在与四方所通型之前的，运行在ARM 335x系统上的，现在已经不再使用，使用了新的STM32平台的车厢控制器。

操作系统: Ubuntu 16.04，使用了overlayfs使得非/home都是只读模式。如果涉及到的操作（如apt安装软件，修改系统配置等）需要改动非/home/目录的文件读写，需要先进去读写模式，然后再操作。

* 程序目录：/home/novotech/app/
* 日志目录：/home/novotech/ntpis160_logs/
* 程序自启动脚本：/home/novotech/.config/autostart/ntpis160.desktop
* 程序启动脚本：ntpis160.start
* 程序停止脚本：ntpis160.stop

== PIS Server ==

服务器IP是192.168.9.200，子网掩码是255.255.0.0，可以通过ssh远程登录，用户名是root，密码是novote.ch

串口外设：

* FM & TEMP Sensor: /dev/ttyS4
* GPS: /dev/ttyACM0
* TOUCH Screen: /dev/ttyS3
* 160KM列车车厢LED屏控制器设备名称: /dev/ttyS2
* TCMS设备名称： /dev/ttyS1

车内屏和车外屏的协议有旧的25T版本和新版本协议。

25T版本的LED屏协议在这里：[[1.帆兴交通设备车号屏通讯协议]]

TCMS与PIS之间的协议见《时速160公里集中动力动车组PIS与TCMS通信协议.doc》文档。

== 系统维护 ==

[[校正160KM PIS主机触摸屏]]
[[160公里车厢控制器更新]]
[[160公里PIS数据包更新]]
[[160KM PIS主机软件更新]]

[[160KM PIS常见问题]]


=== 系统备份 ===

在进行系统备份时，不需要将整个硬盘备份，通过parted工具查看分区end在哪里，然后dd备份。


  sudo dd if=/dev/sdc bs=1G count=27 | gzip > /home/qichunren/img/ntpis160km-2017-06-21.img.gz

=== 系统恢复 ===

在有了dd备份的系统img后，可以通过dd恢复到整个硬盘中：

 gunzip -c /home/qichunren/img/ntpis160km-2017-06-21.img.gz | sudo dd of=/dev/sdc bs=16M

== LED屏控制卡 == 
*控制卡ID说明：1111，车内信息显示屏；0111，车内车号屏；1011，车外屏上半部分，0011，车外屏下半部分。

== 160KM 机箱内部板卡通讯协议 == 
=== CPU 与 FM板卡通讯协议 ===
====物理层协议====
*通过串口通讯，9600波特率，校验位无，数据位8位，停止位1位
=====帧格式表=====
{| class="wikitable sortable"
|-
! 起始位   !! 数据位 !! 校验位置 !! 结束位置
|-
|0xA5,一个字节长度||见数据位表，变长||数据位（从数据位开始到数据位结束）的CRC校验，2个字节长度，低位在前，高位在后||0x5A，一个字节长度
|}

=====数据位表=====
{| class="wikitable sortable"
|-
! 数据来源地址  !! 目的地址 !! 帧序号 !! 数据长度 (第5-6位) !! 数据命令以及数据内容
|-
|0x01表示第一块CPU板，0x02表示第二块CPU板，0x03表示第三块CPU板，'''目前只有一块335X CPU 板子，只用到0x01'''；<br>'''0x81表示FM板子，0x82表示温度传感器板子'''，一个字节||0x01表示第一块CPU板，0x02表示第二块CPU板，0x03表示第三块CPU板'''目前只有一块335X CPU 板子，只用到0x01'''；<br>0x81表示FM板子，0x82表示第二块温度传感器板子'''；<br>0x80表示目的地址是广播给所有的CPU电路板，0xfe表示目的地址是所有的STM32电路板，0xff表示广播给系统中所有设备，其他地址备用，一个字节长度||主动发送数据设备没发送一次数据加一个，两个字节，低位在前，高位在后（大于0xffff，清零），接收需要返回数据同数据返回||数据命令以及数据内容部分数据长度，两个字节，低位在前，高位在后||见数据命令以及数据内容表，变长
|}

=====数据命令以及数据内容表=====      
{| class="wikitable sortable"
|-
! 命令序号（定长，两个字节低位在前，高位在后），第7-8位   !! 命令内容（变长，0到n个字节），第9位开始 !! 是否需要回复（定长，1个字节）
|-
|1，335X读取STM32软件版本||无||1表示需要返回
|-
|2，STM32应答上传软件版本||例如，"S160FM_17103001",15个字节长度||0表示无需返回
|-
|3，335X读取STM32硬件版本||数据长度0|1表示需要返回
|-
|4，STM32应答上传硬件版本||例如，"H160FM_NT1733A1",15个字节长度||0表示无需返回
|-
|5，STM32主动上传软件版本||例如，"S160FM_17103001",15个字节长度||0表示无需返回
|-
|6，335x应答||0表示正常，1表示错误||0表示无需返回
|-
|7，STM32主动上传硬件版本||例如，"H160FM_NT1733A1",15个字节长度||0表示无需返回
|-
|8，335x应答||0表示正常，1表示错误||0表示无需返回
|-
|9，335X下达FM收台命令||0xXX(收台信噪比设置，FM板子上默认值是4，设定范围是1-30，其他无效)，0xXX(收台信号强度设置，FM板子默认值是30，设定范围是10 - 70，其他无效)，一共两个字节||1表示需要返回
|-
|10，STM32 收到收台命令||0表示正常，1表示错误。（收到此命令，且命令正确后，STM32进入收台模式，收台时间需要持续30S左右，在收台过程中，不会响应335x任何命令）||0表示无需返回
|-
|11，STM32发送收台结束后，主动上报收台结果命令||0xXX(0x00，表示FM模块正常，其他表示FM故障),0xXX,一个字节表示找到的电台数量，'''0xXX,0xXX,两个字节，表示电台1的频率，0xXX,电台1信号强度'''；'''0xXX,0xXX,两个字节，表示电台2的频率，0xXX,电台1信号强度'''，依次....||1表示需要返回
|-
|12，335x应答||0表示正常，1表示错误||0表示无需返回
|-
|13，335X下达设置FM声音命令||0xXX，一个字节，0 - 63有效||1表示需要返回
|-
|14，STM32 收到设置FM声音命令||0表示正常，1表示错误。并设置FM声音||0表示无需返回
|-
|15，335X下达设置音源选择命令||0xXX，一个字节，0表示FM音源，'''1表示外接音源并打开外接音源，2表示选择外接音源并关闭外接音源'''，其他无效||1表示需要返回
|-
|16，STM32 设置音源选择命令||0表示正常，1表示错误。并设置音源||0表示无需返回
|-
|17，335X下达获取FM电台信息命令||无||1表示需要返回
|-
|18，STM32上报收台结果命令||0xXX(0x00，表示FM模块正常，其他表示FM故障),0xXX,一个字节表示找到的电台数量，'''0xXX,0xXX,低在前，高在后，两个字节，表示电台1的频率，0xXX,电台1信号强度'''；'''0xXX,0xXX,两个字节，表示电台2的频率，0xXX,电台1信号强度'''，依次....||0表示需要返回
|-
|19，335X下达电台设置命令||0xXX,0xXX,低在前，高在后，两个字节||1表示需要返回
|-
|20，STM32应答||0表示正常，1表示错误（收到正确数据后，FM调频相应频率）||0表示无需返回
|}

=== CPU 与 温度板卡通讯协议 ===
====物理层协议====
*通过串口通讯，9600波特率，校验位无，数据位8位，停止位1位
=====帧格式表=====
{| class="wikitable sortable"
|-
! 起始位   !! 数据位 !! 校验位置 !! 结束位置
|-
|0xA5,一个字节长度||见数据位表，变长||数据位（从数据位开始到数据位结束）的CRC校验，2个字节长度，低位在前，高位在后||0x5A，一个字节长度
|}

=====数据位表=====
{| class="wikitable sortable"
|-
! 数据来源地址  !! 目的地址 !! 帧序号 !! 数据长度 (第5-6位) !! 数据命令以及数据内容
|-
|0x01表示第一块CPU板，0x02表示第二块CPU板，0x03表示第三块CPU板，'''目前只有一块335X CPU 板子，只用到0x01'''；<br>'''0x81表示FM板子，0x82表示温度传感器板子'''，一个字节||0x01表示第一块CPU板，0x02表示第二块CPU板，0x03表示第三块CPU板'''目前只有一块335X CPU 板子，只用到0x01'''；<br>0x81表示FM板子，0x82表示第二块温度传感器板子'''；<br>0x80表示目的地址是广播给所有的CPU电路板，0xfe表示目的地址是所有的STM32电路板，0xff表示广播给系统中所有设备，其他地址备用，一个字节长度||主动发送数据设备没发送一次数据加一个，两个字节，低位在前，高位在后（大于0xffff，清零），接收需要返回数据同数据返回||数据命令以及数据内容部分数据长度，两个字节，低位在前，高位在后||见数据命令以及数据内容表，变长
|}

=====数据命令以及数据内容表=====      
{| class="wikitable sortable"
|-
! 命令序号（定长，两个字节低位在前，高位在后），第7-8位   !! 命令内容（变长，0到n个字节），第9位开始 !! 是否需要回复（定长，1个字节）
|-
|1，335X读取STM32软件版本||无||1表示需要返回
|-
|2，STM32应答上传软件版本||例如，"S160TE_17103001",15个字节长度||0表示无需返回
|-
|3，335X读取STM32硬件版本||数据长度0|1表示需要返回
|-
|4，STM32应答上传硬件版本||例如，"H160TE_NT1734A1",15个字节长度||0表示无需返回
|-
|5，STM32主动上传软件版本||例如，"S160TE_17103001",15个字节长度||0表示无需返回,1表示无需返回
|-
|6，335x应答||0表示正常，1表示错误||0表示无需返回
|-
|7，STM32主动上传硬件版本||例如，"H160TE_NT1734A1",15个字节长度||0表示无需返回,1表示无需返回
|-
|8，335x应答||0表示正常，1表示错误||0表示无需返回
|-
|9，STM32主动上传温度||0xXX,0xXX，温度ADC值（低位在前，高位在后），0xXX,0xXX，温度ADC校正值（详见'''温度ADC校正值说明'''），0xXX,0xXX，上传温度值(低位在前，高位在后，温度为：（温度值 - 60)，比如上传温度值为91 ，则实际温度为（91 - 60 = 31摄氏度),0xXX,表示传感器状态（0x00，传感器OK,0x01，传感器故障）||0表示无需返回,1表示无需返回
|-
|10，335x应答||0表示正常，1表示错误||0表示无需返回
|-
|11，335X设置传感器ADC校正值||0xXX,0xXX，温度ADC校正值（详见'''温度ADC校正值说明'''）||1表示需要返回
|-
|12，STM32应答||一个长度，0x00表示数据正常，其他表示数据异常||0表示无需返回
|}

=====温度ADC校正值=====    
'''数据说明：在1000欧姆时（精密电阻1000欧姆档，或恒温槽0度时，精密PT1000），下位机(stm32)上传温度AD采集原始值与1000欧姆时AD标准值（标准AD值为 ：2048）的差值，如果下位机上传的 是 2058，差值为 2048 - 2065 = -17，则上位机发送的相应通道的校正AD值为：0x111 （其中，第一个1表示 实际值比标准值大，后面0x11 即为 17），如果下位机上传的 是 1731，差值为 2048 - 1976 = 72，则上位机发送的相应通道的校正AD值为：0x048 （其中，第一个0表示 实际值比标准值小，后面0x48 即为72），如果 误差值 大于 255，则认为硬件有故障，不可校正，同时下位机如果收到校正值大于0x1ff，认为不是校正值，不予处理。低位在前，高位在后'''

=== PIS主控站 与 车厢控制器通讯协议（车厢控制器code升级部分） ===
=====数据命令以及数据内容表=====      
{| class="wikitable sortable"
|-
! 主控站ID（定长，1个字节   !! 车厢控制器ID（定长，1个字节   !! 命令序号（定长，1个字节  ） !! 命令内容（变长，0到n个字节）
|-
|0x81，不变||同车厢所在车厢号||1，主控站获取车厢控制器软件版本号||无
|-
|0x81，不变||同车厢所在车厢号||2，STM32应答||例如，"LJTCC1710182"，12个字节长度
|-
|0x81，不变||同车厢所在车厢号||3，PIS主机提出升级||无
|-
|0x81，不变||同车厢所在车厢号||4，STM32应答||0，数据正常，1，数据错误
|-
|0x81，不变||同车厢所在车厢号||5，PIS主机发送代码数据长度给STM32||4个字节长度,低位在前，高位在后
|-
|0x81，不变||同车厢所在车厢号||6，STM32应答||0，数据正常，1，数据错误
|-
|0x81，不变||同车厢所在车厢号||7，PIS主机发送代码数据给STM32||'''见PIS发送代码数据给STM32数据内容表'''
|-
|0x81，不变||同车厢所在车厢号||8，STM32应答||0xXXXX，代码包序号，低位在前，高位在后)，0xXX(0，数据正常，1，数据错误),一共3个字节
|-
|0x81，不变||同车厢所在车厢号||9，PIS主机发送传输数据完毕命令||无
|-
|0x81，不变||同车厢所在车厢号||10，STM32应答||0，数据正常，1，数据错误
|-
|0x81，不变||同车厢所在车厢号||11，PIS主机发送强行结束升级命令||无
|-
|0x81，不变||同车厢所在车厢号||12，STM32应答||0，数据正常，1，数据错误
|}
=====车厢控制器返回主控站LED屏控制卡软件版本号（使用显示信息命令0x02）=====      
{| class="wikitable sortable"
|-
! 主控站ID（定长，1个字节   !! 车厢控制器ID（定长，1个字节   !! 命令序号（定长，1个字节  ） !! 命令内容（变长，0到n个字节）
|-
|0x81，不变||同车厢所在车厢号||0x01，表示反馈LED控制卡软件版本号||每个STM32软件版本号字长8个字节，每个版本号之间使用''','''隔开，一共16个STM32软件版本，总字节长度是144，软件版本依次为：ID1车外屏上部分，ID1车外屏下部分,ID2车外屏上部分，ID2车外屏下部分,ID3车外屏上部分，ID3车外屏下部分,ID4车外屏上部分，ID4车外屏下部分,ID8坐车车内信息显示屏，ID8坐车车内车号屏,ID9坐车车内信息显示屏，ID9坐车车内车号屏,ID5窝车车内信息显示屏，ID5窝车车内车号屏,ID6窝车车内信息显示屏，ID6窝车车内车号屏
|}

=====PIS发送代码数据给STM32数据内容表=====
{| class="wikitable sortable"
|-
! 代码包序号 !! 代码数据 !! 校验 
|-
|0x0001，两个字节，低位在前，表示第一包数据，依次递增||代码数据，128个字节，不够128字节补充0xff||代码数据crc16校验，2个字节，低位在前，高位在后
|}

=== PIS主控站 与 车厢控制器通讯协议（设置车厢内外屏亮度命令） ===
=====设置亮度命令=====      
{| class="wikitable sortable"
|-
! SXT (4个字节)!!Length HIGH, Length HIGH（两个字节）!! CMD（1个字节）!! Device TYPE（1个字节）!!Device ID（1个字节）!!亮度（1个字节）
|-
! 77 FF AA 09!!0x00 0x01!! 0x56（表示设置屏亮度命令）!!屏类型!!屏地址!!亮度，1-9有效，其他无效
|-
|}
=====设置清除屏保存数据mil=====      
{| class="wikitable sortable"
|-
! SXT (4个字节)!!Length HIGH, Length HIGH（两个字节）!! CMD（1个字节）!! Device TYPE（1个字节）!!Device ID（1个字节）!!清除保存数据区域（1个字节）
|-
! 77 FF AA 09!!0x00 0x01!! 0x3d（表示设置屏亮度命令）!!屏类型!!屏地址!!显示区域
|-
|}

=====设置显示模式命令=====      
{| class="wikitable sortable"
|-
! SXT (4个字节)!!Length HIGH, Length HIGH（两个字节）!! CMD（1个字节）!! Device TYPE（1个字节）!!Device ID（1个字节）!!亮度（1个字节）
|-
! 77 FF AA 09!!0x00 0x01!! 0x58（表示设置显示模式命令）!!屏类型!!屏地址!!1，表示立即更新显示，2，表示滚动完毕更新显示，其他无效
|-
|}
|}

=== 车外屏ID设置命令 ===
CMD:0x59;Display mode:第一个字节当前ID(0到99，或者0xff，所有的都设置)，第二个字节设置新的ID(0到99，或者0xff（恢复为硬件ID）)，第三个字节选择已经设置ID是否可以再设置（0不设置，1设置）

==PIS 160KM字库（字库扫描方式都是从上到下，从左到右）==
*制作字库24点阵ASC字库，24点阵汉子字库（不带全角标点符号），24点阵汉字全角标点符号；
**24点阵ASC字库，生成字库文件名为'''ASC24'''，每一个字符点个数是24*12个点，占字库空间大小是24*16/8 = 48个字节，前面36个字节有效，后面12个字节填0x00，字符字库在文件中的存储位置是：ch*48，比如说：字符A(0x41)，在文件 ASC24 中位置是 65*48 = 3120处开始存储
**24点阵汉子字库（不带全角标点符号），生成字库文件名为'''HZK24S''',每一个汉字字模点个数为24*24个点，占字库空间大小是24*24/8 = 72个字节。字符在字库文件中的存储位置是：
'''L = (*unicode & 0x00FF)-0xA0;H = ((*unicode & 0xFF00)>>8)-0xA0; P(位置) = ((L - 1 - 15) * 94 + (H - 1)) * 72'''
**24点阵汉字全角标点符号，生产字库文件名为'''HZK24T''',每一个符号字模点个数为24*24个点，占字库空间大小是24*24/8 = 72个字节。字符在字库文件中的存储位置是：
'''L = (*unicode & 0x00FF)-0xA0;H = ((*unicode & 0xFF00)>>8)-0xA0; P(位置) = L = ((L - 1) * 94 + (H - 1)) * 72; '''
*制作32点阵ASC字库，32点阵汉子字库（包含全角标点符号）；
**32点阵ASC字库，生成字库文件名为'''ASC32'''，每一个字符点个数是32*16个点，占字库空间大小是32*16/8 = 64个字节，字符字库在文件中的存储位置是：ch*64，比如说：字符A(0x41)，在文件 ASC24 中位置是 65*64 = 4160处开始存储
**32点阵汉子字库（带全角标点符号），生成字库文件名为'''HZK32''',每一个汉字字模点个数为32*32个点，占字库空间大小是32*32/8 = 128个字节。字符在字库文件中的存储位置是：
'''L = (*unicode & 0x00FF)-0xA0;H = ((*unicode & 0xFF00)>>8)-0xA0; P(位置) = (((L - 1) * 94 + (H - 1)) * 128'''
**制作48点阵ASC字库。生成字库文件名为'''ASC48'''，每一个字符点个数是32*16个点，占字库空间大小是32*16/8 = 64个字节，字符字库在文件中的存储位置是：ch*144，比如说：字符A(0x41)，在文件 ASC24 中位置是 65*144 = 9216处开始存储
==PIS160 售后维修记录==
*1、2019年1月23日，160车杭州开往北京，出现第4、3、2、1 四节车厢出现显示时有时无现象，于1月25日在杭州，断开5车厢与4车厢连接，5车厢485线正常（数据线D+,D-电压在5V左右），5车厢后485线异常（数据线D+,D-电压在0V左右），再到1车厢，断开1、2车厢485线，1车厢485线正常，1车厢后485线异常，再到2车厢检测，情况同1车厢，再到3车厢检测，断开3、4车厢线，3车厢输入485线正常，3车厢输出485线异常，判断4车厢输入信号正常，3车厢输出信号异常，断开3、2车厢连线，单测试3车厢  车厢控制器 485线信号异常，判断3车厢  车厢控制器 故障。将3车厢 车厢控制器 换为新技售后备用车厢控制器，一切现实正常。'''另注：3车厢 车厢控制器 为莱德生产'''
*2、板子上有一颗 0805 封装的 2欧姆 限流电阻故障：
造成原因：
1、车体可能会有过大干扰，引起电阻失效；
2、车上有车厢控制器出现短路等故障，导致源端（PIS主机485板）限流保护电阻失效
3、电阻单体质量原因，导致电阻失效；

预防措施：
使用过电流能力更强电阻，或者使用自恢复保险丝

==PIS160 售后管理维护==
===数据文件的备份与更新===
*备份语音文件：在U盘的根目录下创建NTPIS-ACTION.txt文件,在文件中编辑文本export_audios并保存，将U盘插入PIS主机的USB接口即可导出语音文件(*语音文件较多时，导出时间较长，在显示导出语音文件完成后再拔出U盘).
*备份数据库文件：在U盘的根目录下创建NTPIS-ACTION.txt文件,在文件中编辑文本export_db并保存，将U盘插入PIS主机的USB接口即可导出数据库文件(*在显示导出数据库文件成功后再拔出U盘).
*更新数据库文件：在U盘的根目录下创建NTPIS-UPDATE.txt文件,同时将要更新的数据库文件(**.db)放在同级目录下，将U盘插入PIS主机的USB接口，然后根据界面提示进行选择更新，PIS主机自动重启即可完成数据库更新.
*更新语音文件：
**更新单个语音文件：在U盘的根目录下创建NTPIS-UPDATE.txt文件,同时将要上传的语音文件(*.wav/*.mp3)放在同级目录下，将U盘插入PIS主机的USB接口，然后根据界面提示进行选择更新，根据更新界面显示的日志即可判断是否更新成功.
**更新多个语音文件：在U盘的根目录下创建NTPIS-UPDATE.txt文件，同时将要上传的多个语音文件进行打包(*.zip)放在同级目录下，将U盘插入PIS主机的USB接口，然后根据界面提示进行选择更新，PIS主机自动重启即可完成多个语音包更新.
'''备份与更新时标识文件不一样，标识文件不能同时存在.'''

'''更新时注意可使用的存储空间(df -h).'''
===主程序的更新===
从 http://192.168.8.29:81/aria-160km-pis-server-mk2-buildroot/ 找到 对应的 160km-pis-server-update-{日期时间戳}.tar.xz 文件，将文件下载到本地，解压，其中的 app 目录里包括了主程序和相关的所有脚本.
*单独更新主程序：只需要将新版主程序文件 ntpis160 和标识文件 NTPIS-UPDATE.txt 一同放在 U 盘根目录中即可，然后根据界面提示进行选择更新，PIS主机重启即可完成主程序更新.
*更新主程序及其其它变动文件：创建一个文件夹(eg:ntpis160-update)，将主程序ntpis160文件放入其中，在其中创建files文件夹(将要变动的文件放入其中)，之后创建一个名为 post_update.sh 脚本文件(它在软件更新时会被自动执行，在这个脚本中可以编写除了更新 ntpis160 主程序以外的其它操作),将其打包为zip格式的压缩包(软件更新包)；将软件更新包(*.zip)和标志文件NTPIS-UPDATE.txt放在U盘根目录下，完成更新.
'''注意查看系统版本是否变为预期版本'''
===屏幕校准===
*在U盘根目录下创建NTPIS-TOUCH.txt文件，将U盘插入PIS主机USB接口，根据提示操作即可
===查看系统运行状态===
*这些命令需要电脑与PIS主机网口建立通信，然后通过putty软件ssh进行远程登录输入查看
**fdisk -l ---查看所有磁盘存储空间
**df -h    ---查看用户可以使用的存储空间(与磁盘存储总空间差距太大时可扩展)
**top      ---查看当前所有进程运行的状态
**ps -ef   ---查看主机所有进程